#include "Geometry/Frustum.h"


namespace tyro
{
	namespace Geometry
	{

		Frustum::Frustum()
			:	FOVy(Math::PI/4.0f)
			,	aspectRatio(1.333333f)
			,	focalLength(10.0f)
			,	nearVal(1.0f)
			,	farVal(1000000.0f)
			,	projectionMatrix(Math::Matrix4f::IDENTITY)
			,	projectionType(PT_PERSPECTIVE)
			,	recalcFrustrum(true)
		{

		}

		Frustum::~Frustum()
		{

		}

		void Frustum::Update( void ) const
		{
			if(IsFrustrumDirty())
			{
				CalculateExtents();

				CalculateProjection();
			}

		}

		void Frustum::CalculateExtents( void ) const
		{
			FLOAT_32 newLeft, newRight, newBottom, newTop;

			if (projectionType == PT_PERSPECTIVE)
			{
				Math::Radian<FLOAT_32> thetaY(FOVy * 0.5f);
				FLOAT_32 tanThetaY = Math::Tan(thetaY);
				FLOAT_32 tanThetaX = tanThetaY * aspectRatio; 

				FLOAT_32 nearFocal = nearVal / focalLength;
				FLOAT_32 nearOffsetX = frustumOffset.x * nearFocal;
				FLOAT_32 nearOffsetY = frustumOffset.y * nearFocal;

				FLOAT_32 halfWidth = tanThetaX * nearVal;
				FLOAT_32 halfHeight = tanThetaY * nearVal;

				newLeft = - halfWidth + nearOffsetX;
				newRight = + halfWidth + nearOffsetX;
				newBottom = - halfHeight + nearOffsetY;
				newTop = halfHeight + nearOffsetY;

				this->left = newLeft;
				this->right = newRight;
				this->top = newTop;
				this->bottom = newBottom;
			}
			
			recalcFrustrum = false;
		}

		void Frustum::CalculateProjection( void ) const
		{
			FLOAT_32 inv_w = 1 / (right - left);
			FLOAT_32 inv_h = 1 / (top - bottom);
			FLOAT_32 inv_d = 1 / (farVal - nearVal);

			if (projectionType == PT_PERSPECTIVE)
			{
				FLOAT_32 A = 2 * nearVal * inv_w;
				FLOAT_32 B = 2 * nearVal * inv_h;
				FLOAT_32 C = (right + left) * inv_w;
				FLOAT_32 D = (top + bottom) * inv_h;
				FLOAT_32 q, qn;

				q = - (farVal + nearVal) * inv_d;
				qn = -2 * (farVal * nearVal) * inv_d;

				projectionMatrix = Math::Matrix4f::ZERO;
				projectionMatrix.m00 = A;
				projectionMatrix.m20 = C;
				projectionMatrix.m11 = B;
				projectionMatrix.m21 = D;
				projectionMatrix.m22 = q;
				projectionMatrix.m32 = qn;
				projectionMatrix.m23 = -1;
			}
		}

		Math::Matrix4f Frustum::GetProjectionMatrix() const
		{
			return projectionMatrix;
		}

		bool Frustum::IsFrustrumDirty() const
		{
			return recalcFrustrum;
		}

		



	}
}